---
id: testing-vercel-functions-with-opentelemetry-tracetest
title: Testing Vercel Functions (Next.js) with OpenTelemetry and Tracetest
description: Quick start on how to configure Vercel functions with OpenTelemetry and Tracetest for enhancing your integration tests with trace-based testing.
hide_table_of_contents: false
keywords:
  - tracetest
  - trace-based testing
  - observability
  - distributed tracing
  - testing
  - vercel
  - nextjs
  - end to end testing
  - end-to-end testing
  - integration testing
  - serverless testing
  - testing serverless functions
  - testing vercel functions
  - vercel testing
  - opentelemetry
image: https://res.cloudinary.com/djwdcmwdz/image/upload/v1698686403/docs/Blog_Thumbnail_14_rsvkmo.jpg
---

:::note
[Check out the source code on GitHub here.](https://github.com/kubeshop/tracetest/tree/main/examples/integration-testing-vercel-functions)
:::

[Tracetest](https://tracetest.io/) is a testing tool based on [OpenTelemetry](https://opentelemetry.io/) that allows you to test your distributed application. It allows you to use data from distributed traces generated by OpenTelemetry to validate and assert if your application has the desired behavior defined by your test definitions.

[Vercel](https://vercel.com/) is a platform that hosts serverless functions and front-end code offering developers scalability and flexibility with no infrastructure overhead.

## Why is this important?

Testing Serverless Functions has been a pain point for years. Not having visibility into the infrastructure and not knowing where a test fails causes the MTTR to be higher than for other tools. Including OpenTelemetry in Vercel functions exposes telemetry that you can use for both production visibility and trace-based testing.

This sample shows how to run integration tests against Vercel Functions using [OpenTelemetry](https://opentelemetry.io/) and Tracetest.

The Vercel function will fetch data from an external API, transform the data and insert it into a Vercel Postgres database. This particular flow has two failure points that are difficult to test.

1. Validating that an external API request from a Vercel function is successful.
2. Validating that a Postgres insert request is successful.

## Prerequisites

**Tracetest Account**:

- Sign up to [`app.tracetest.io`](https://app.tracetest.io) or follow the [get started](/getting-started/overview) docs.
- Create an [environment](/concepts/environments).
- Create an [environment token](/concepts/environment-tokens).
- Have access to the environment's [agent API key](/configuration/agent).
- [Vercel Account](https://vercel.com/)
- [Vercel Postgres Database](https://vercel.com/docs/storage/vercel-postgres)

**Vercel Functions Example:**

Clone the [Tracetest GitHub Repo](https://github.com/kubeshop/tracetest) to your local machine, and open the Vercel example app.

```bash
git clone https://github.com/kubeshop/tracetest.git
cd tracetest/examples/integration-testing-vercel-functions
```

Before moving forward, run `npm i` in the root folder to install the dependencies.

```bash
npm i
```

**Docker**:

- Have [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed on your machine.

## Project Structure

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

It's using Vercel Functions via `/pages/api`, with [OpenTelemetry configured as explained in the Vercel docs](https://nextjs.org/docs/pages/building-your-application/optimizing/open-telemetry#manual-opentelemetry-configuration).

### 1. Vercel (Next.js) Function

The `docker-compose.yaml` file reference the Next.js app with `next-app`.

### 2. Tracetest

The `docker-compose.yaml` file also has a Tracetest Agent service and an integration tests service.

### Docker Compose Network

All `services` in the `docker-compose.yaml` are on the same network and will be reachable by hostname from within other services. E.g. `next-app:3000` in the `test/api.pokemon.spec.docker.yaml` will map to the `next-app` service.

## Vercel (Next.js) Function

The Vercel Function is a simple API, [contained in the `pages/api/pokemon.ts` file](https://github.com/kubeshop/tracetest/blob/main/examples/integration-testing-vercel-functions/pages/api/pokemon.ts).

```typescript
import { trace, SpanStatusCode } from '@opentelemetry/api'
import type { NextApiRequest, NextApiResponse } from 'next'
import { sql } from '@vercel/postgres'

export async function addPokemon(pokemon: any) {
  return await sql`
    INSERT INTO pokemon (name)
    VALUES (${pokemon.name})
    RETURNING *;
  `
}

export async function getPokemon(pokemon: any) {
  return await sql`
    SELECT * FROM pokemon where id=${pokemon.id};
  `
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const activeSpan = trace.getActiveSpan()
  const tracer = await trace.getTracer('integration-testing-vercel-functions')
  
  try {

    const externalPokemon = await tracer.startActiveSpan('GET Pokemon from pokeapi.co', async (externalPokemonSpan) => {
      const requestUrl = `https://pokeapi.co/api/v2/pokemon/${req.body.id || '6'}`
      const response = await fetch(requestUrl)
      const { id, name } = await response.json()

      externalPokemonSpan.setStatus({ code: SpanStatusCode.OK, message: String("Pokemon fetched successfully!") })
      externalPokemonSpan.setAttribute('pokemon.name', name)
      externalPokemonSpan.setAttribute('pokemon.id', id)
      externalPokemonSpan.end()

      return { id, name }
    })

    const addedPokemon = await tracer.startActiveSpan('Add Pokemon to Vercel Postgres', async (addedPokemonSpan) => {
      const { rowCount, rows: [addedPokemon, ...rest] } = await addPokemon(externalPokemon)
      addedPokemonSpan.setAttribute('pokemon.isAdded', rowCount === 1)
      addedPokemonSpan.setAttribute('pokemon.added.name', addedPokemon.name)
      addedPokemonSpan.end()
      return addedPokemon
    })
    
    res.status(200).json(addedPokemon)

  } catch (err) {
    activeSpan?.setAttribute('error', String(err))
    activeSpan?.recordException(String(err))
    activeSpan?.setStatus({ code: SpanStatusCode.ERROR, message: String(err) })
    res.status(500).json({ error: 'failed to load data' })
  } finally {
    activeSpan?.end()
  }
}
```

The OpenTelemetry tracing is [contained in the `instrumentation.node.ts` file](https://github.com/kubeshop/tracetest/blob/main/examples/integration-testing-vercel-functions/instrumentation.node.ts). Traces will be sent to the Tracetest Agent.

```typescript
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'

const sdk = new NodeSDK({
  // The OTEL_EXPORTER_OTLP_ENDPOINT env var is passed into "new OTLPTraceExporter" automatically.
  // If the OTEL_EXPORTER_OTLP_ENDPOINT env var is not set the "new OTLPTraceExporter" will
  // default to use "http://localhost:4317" for gRPC and "http://localhost:4318" for HTTP.
  // This sample is using HTTP.
  traceExporter: new OTLPTraceExporter(),
  instrumentations: [
    getNodeAutoInstrumentations(),
    new FetchInstrumentation(),
  ],
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'integration-testing-vercel-functions',
  }),
})
sdk.start()
```

### Set up Environment Variables

Edit the `.env.development` file. Add your Vercel Postgres env vars.

```bash title=.env.development.local
# OTLP HTTP
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"

# Vercel Postgres
POSTGRES_DATABASE="**********"
POSTGRES_HOST="**********"
POSTGRES_PASSWORD="**********"
POSTGRES_PRISMA_URL="**********"
POSTGRES_URL="**********"
POSTGRES_URL_NON_POOLING="**********"
POSTGRES_USER="**********"
```

### Start the Next.js Vercel Function

Spin up your Next.js app.

```bash
npm run dev
```

This starts the function on `http://localhost:3000/api/pokemon`.

## Testing the Vercel Function Locally

[Download the CLI](/getting-started/install-cli) for your operating system.

The CLI is bundled with [Tracetest Agent](/concepts/agent/) that runs in your infrastructure to collect responses and traces for tests.

To start Tracetest Agent add the `--api-key` from your environment.

```bash title=Terminal
tracetest start --api-key YOUR_AGENT_API_KEY
```

Run a test with the test definition `test/api.pokemon.spec.development.yaml`.

```yaml title=test/api.pokemon.spec.development.yaml
type: Test
spec:
  id: kv8C-hOSR
  name: Test API
  trigger:
    type: http
    httpRequest:
      method: POST
      url: http://localhost:3000/api/pokemon
      body: "{\n  \"id\": \"6\"\n}"
      headers:
      - key: Content-Type
        value: application/json
  specs:
  - selector: span[tracetest.span.type="http"]
    name: "All HTTP Spans: Status  code is 200"
    assertions:
    - attr:http.status_code = 200
```

```bash title=Terminal
tracetest run test -f ./test/api.pokemon.spec.development.yaml --required-gates test-specs --output pretty

[Output]
✔ Test API (https://app.tracetest.io/organizations/<YOUR_ORG>/environments/<YOUR_ENV>/test/-gjd4idIR/run/22/test) - trace id: f2250362ff2f70f8f5be7b2fba74e4b2
    ✔ All HTTP Spans: Status code is 200
```

## Integration Testing the Vercel Function

Edit the `.env.docker` file to use your Vercel Postgres env vars.

```bash title=.env.docker
# OTLP HTTP
OTEL_EXPORTER_OTLP_ENDPOINT="http://tracetest-agent:4318"

# Vercel Postgres
POSTGRES_DATABASE="**********"
POSTGRES_HOST="**********"
POSTGRES_PASSWORD="**********"
POSTGRES_PRISMA_URL="**********"
POSTGRES_URL="**********"
POSTGRES_URL_NON_POOLING="**********"
POSTGRES_USER="**********"
```

This configures the `OTEL_EXPORTER_OTLP_ENDPOINT` to send traces to Tracetest Agent.

Edit the `docker-compose.yaml` in the root directory. Add your `TRACETEST_API_KEY`.

```yaml title=docker-compose.yaml
  # [...]
  tracetest-agent:
    image: kubeshop/tracetest-agent:latest
    environment:
      - TRACETEST_API_KEY=YOUR_TRACETEST_API_KEY # Find the Agent API Key here: https://docs.tracetest.io/configuration/agent
    ports:
      - 4317:4317
      - 4318:4318
    networks:
      - tracetest
```

Edit the `run.bash`. Add your `TRACETEST_API_TOKEN`.

```bash
#/bin/bash

# Find the API Token here: https://docs.tracetest.io/concepts/environment-tokens
tracetest configure -t YOUR_TRACETEST_API_TOKEN
tracetest run test -f ./api.pokemon.spec.docker.yaml --required-gates test-specs --output pretty
```

Now you can run the Vercel function and Tracetest Agent!

```bash
docker compose up -d --build
```

And, trigger the integration tests.

```bash
docker compose run integration-tests

[Ouput]
[+] Creating 1/0
 ✔ Container integration-testing-vercel-functions-tracetest-agent-1  Running                                                                                             0.0s
 SUCCESS  Successfully configured Tracetest CLI
✔ Test API (https://app.tracetest.io/organizations/<YOUR_ORG>/environments/<YOUR_ENV>/test/p00W82OIR/run/8/test) - trace id: d64ab3a6f52a98141d26679fff3373b6
    ✔ All HTTP Spans: Status code is 200
```

## Learn More

Feel free to check out our [examples in GitHub](https://github.com/kubeshop/tracetest/tree/main/examples) and join our [Slack Community](https://dub.sh/tracetest-community) for more info!
